home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Snippets / Toolbox / AddResMenu7.1 / Source / AddResMenu7.1.c
Encoding:
C/C++ Source or Header  |  1996-09-17  |  35.8 KB  |  921 lines  |  [TEXT/CWIE]

  1. /* MyAddResMenu */
  2. /* There is a minor bug in system 7.1 (oh no!) */
  3. /* AppendResMenu will only alphabatize 'FONT' resources, all others */
  4. /* will just be displayed in the order they appear in the resource map */
  5. /* of the resource fork. */
  6. /* This isn't harmful, it's just a little ugly. */
  7. /* Fixing it is pretty simple, just use the function in this sample */
  8. /* called MyAddResMenu */
  9. /* C.K. Haun */
  10. /* Apple DTS */
  11. /* Of course, Copyright 1991-1992, Apple Computer Inc. */
  12.  
  13. #include <Events.h>
  14. #include <SegLoad.h>
  15. #include "AddResMenu7.1.h"
  16.  
  17. /* prototypes */
  18.  
  19. void InitalizeApp(void);
  20. void DoDiskEvents(long dinfo);                              /* hi word is error code, lo word is drive number */
  21. void DrawMain(WindowPtr drawIt);
  22. Boolean DoSelected(long val);
  23. void SizeMain(WindowPtr theWindow);
  24. void InitAEStuff(void);
  25. void DoHighLevel(EventRecord *AERecord);
  26. void DoDaCall(MenuHandle themenu, long theit);
  27. void DoDocumentClick();
  28.  
  29. pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  30. pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  31. pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  32. pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn);
  33.  
  34. void SampleHelpDialog(void);
  35.  
  36. WindowPtr AddNewWindow(short theID);
  37. short HasSelectionRange(DialogPeek inputDialog);
  38. Boolean IsEditKey(char theKey, short modifiers);
  39. void AddAResourceType(void);
  40. void MyAddResMenu(ResType theType, MenuHandle theMenu, short afterItem);
  41. pascal Boolean filterIt(DialogPtr inputDialog, EventRecord *myDialogEvent, short *theDialogItem);
  42. ControlHandle SnatchHandle(DialogPtr thebox, short theGetItem);
  43. void CleanUpMetaChars(Str255 theString);
  44. void NilProc(void);
  45. Boolean TabRetEsc(DialogPtr inputDialog, EventRecord *myDialogEvent, short *theDialogItem);
  46. /* one external */
  47. #ifdef applec
  48. extern void _DataInit();                                    /* this is the C initialization code */
  49. #endif
  50.  
  51. /* globals */
  52. Boolean gQuit, gInBackground;
  53. unsigned long gMySleep;
  54. ProcessSerialNumber gOurSN;
  55. short gHelpItem;
  56. ModalFilterUPP gModalFilterUPP;
  57.  
  58. #ifdef powerc
  59.    QDGlobals    qd;
  60. #endif
  61.  
  62. #pragma segment Main
  63. void main(void)
  64. {
  65.     EventRecord myEventRecord;
  66.     WindowPtr twindow;
  67.     short fHit;
  68.     windowCHandle tempWCH;
  69.     
  70.     #ifdef applec
  71.     UnloadSeg((Ptr)_DataInit);                              /* throw out setup code */
  72.     #endif
  73.     
  74.     InitalizeApp();
  75.     UnloadSeg((Ptr)InitalizeApp);                           /* get rid of my initialization code */
  76.     do {
  77.         
  78.         WaitNextEvent(everyEvent, &myEventRecord, gMySleep, nil);
  79.         switch (myEventRecord.what) {
  80.             case nullEvent:
  81.                 /* no nul processing in this sample */
  82.                 break;
  83.             case updateEvt:
  84.                 /* always check to see if it's my window */
  85.                 /* this may not seem necessary under 7.0, where it's unlikely or impossible for */
  86.                 /* a DA to be in your layer, but there are others*/
  87.                 /* who can stick themselves into your window list, */
  88.                 /* BalloonWriter comes quickly to mind */
  89.                 
  90.                 if (((WindowPeek)myEventRecord.message)->windowKind == kMyDocumentWindow) {
  91.                     tempWCH = (windowCHandle)GetWRefCon((WindowPtr)myEventRecord.message);
  92.                     (ProcPtr)((*tempWCH)->drawMe)((WindowPtr)myEventRecord.message);
  93.                 }
  94.                 break;
  95.             case mouseDown:
  96.                 /* first see where the hit was */
  97.                 fHit = FindWindow(myEventRecord.where, &twindow);
  98.                 
  99.                 switch (fHit) {
  100.                     Rect limitRect;
  101.                     Str255 tempString;
  102.                     long back;
  103.                     case inDesk:                            /* if they hit in desk, then the process manager */
  104.                         break;                              /* will switch us out, we don't need to do anything */
  105.                     case inMenuBar:
  106.                         DoSelected(MenuSelect(myEventRecord.where));
  107.                         break;
  108.                         
  109.                     case inSysWindow:
  110.                         
  111.                         /* pass to the system */
  112.                         SystemClick(&myEventRecord, twindow);
  113.                         break;
  114.                     case inContent:
  115.                         
  116.                         /* Handle content and control clicks here */
  117.                         
  118.                         if (FrontWindow()) {                /* don't do this unless we have a window open, silly */
  119.                             windowCHandle clicker;
  120.                             if (((WindowPeek)twindow)->windowKind == kMyDocumentWindow) {
  121.                                 
  122.                                 clicker = (windowCHandle)GetWRefCon(twindow);
  123.                                 /* jump to the content function stored for this window */
  124.                                 HLock((Handle)clicker);     /* lock it down so things don't get stupid */
  125.                                 (ProcPtr)((*clicker)->clickMe)(twindow);
  126.                                 HUnlock((Handle)clicker);       /* all done */
  127.                             }
  128.                         }
  129.                         break;
  130.                     case inDrag:
  131.                         DragWindow(twindow, myEventRecord.where, &qd.screenBits.bounds);
  132.                         break;
  133.                     case inGrow:
  134.                         /* Call GrowWindow here if you have a grow box */
  135.                         SetPort(twindow);
  136.                         limitRect = qd.screenBits.bounds;
  137.                         limitRect.top = kMinHeight;
  138.                         GetWTitle(twindow, tempString);
  139.                         /* I'm not letting the user shrink the window so */
  140.                         /* small that the title is truncated */
  141.                         limitRect.left = StringWidth(tempString) + 120;
  142.                         back = GrowWindow(twindow, myEventRecord.where, &limitRect);
  143.                         
  144.                         if (back) {
  145.                             windowCHandle tempWCH = (windowCHandle)GetWRefCon(twindow);
  146.                             Rect sizeRect = ((WindowPtr)twindow)->portRect;
  147.                             InvalRect(&sizeRect);
  148.                             sizeRect.top = sizeRect.bottom - 16;
  149.                             sizeRect.left = sizeRect.right - 16;
  150.                             EraseRect(&sizeRect);
  151.                             InvalRect(&sizeRect);
  152.                             SizeWindow(twindow, back & 0xffff, back >> 16, true);
  153.                             (ProcPtr)((*tempWCH)->sizeMe)(twindow);
  154.                         }
  155.                         InvalRect(&twindow->portRect);
  156.                         
  157.                         break;
  158.                     case inGoAway:
  159.                         /* Click in Close box */
  160.                         if (TrackGoAway(twindow, myEventRecord.where)) {
  161.                             if (((WindowPeek)twindow)->windowKind == kMyDocumentWindow)
  162.                                 (ProcPtr)((*(windowCHandle)((WindowPeek)twindow)->refCon)->closeMe)(twindow);
  163.                         }
  164.                         break;
  165.                     case inZoomIn:
  166.                     case inZoomOut:
  167.                         if (TrackBox(twindow, myEventRecord.where, fHit)) {
  168.                             windowCHandle tempWCH = (windowCHandle)GetWRefCon(twindow);
  169.                             SetPort(twindow);
  170.                             
  171.                             ZoomWindow(twindow, fHit, true);
  172.                             InvalRect(&twindow->portRect);
  173.                             if (((WindowPeek)twindow)->windowKind == kMyDocumentWindow)
  174.                                 (ProcPtr)((*tempWCH)->sizeMe)(twindow);
  175.                         }
  176.                 }
  177.             case mouseUp:
  178.                 /* don't care */
  179.                 break;
  180.                 /* same action for key or auto key */
  181.             case keyDown:
  182.             case autoKey:
  183.                 if (myEventRecord.modifiers & cmdKey)
  184.                     DoSelected(MenuKey(myEventRecord.message & charCodeMask));
  185.                 break;
  186.             case keyUp:
  187.                 /* don't care */
  188.                 break;
  189.             case diskEvt:
  190.                 
  191.                 /* I don't do anything special for disk events, this just passes them */
  192.                 /* to a function that checks for an error on the mount */
  193.                 DoDiskEvents(myEventRecord.message);
  194.                 break;
  195.             case activateEvt:
  196.                 
  197.                 if (myEventRecord.modifiers & activeFlag) {
  198.                     if (((WindowPeek)myEventRecord.message)->windowKind == kMyDocumentWindow) {
  199.                         
  200.                         tempWCH = (windowCHandle)GetWRefCon((WindowPtr)myEventRecord.message);
  201.                         (ProcPtr)((*tempWCH)->drawMe)((WindowPtr)myEventRecord.message);
  202.                     }
  203.                 }
  204.                 break;
  205.             case 10:
  206.                 /* don't care */
  207.                 break;
  208.             case 11:
  209.                 /* don't care */
  210.                 break;
  211.             case 15:
  212.                 switch ((myEventRecord.message >> 24) & 0x0FF) {        /* high byte of message */
  213.                     case suspendResumeMessage:              /* suspend/resume is also an activate/deactivate */
  214.                         gInBackground = (myEventRecord.message & kResumeMask) == 0;
  215.                         break;
  216.                 }
  217.                 break;
  218.             case kHighLevelEvent:
  219.                 DoHighLevel(&myEventRecord);
  220.                 break;
  221.             default:
  222.                 break;
  223.                 /* This dispatches high level events (AppleEvents, for example) */
  224.                 /* to our dispatch routine.This is NEW in the event loop for */
  225.                 /* System 7 */
  226.         }
  227.     }
  228.             while(gQuit != true);
  229.     
  230. }
  231.  
  232. /* DoDaCall opens the requested DA.It's here as a seperate routine if you'd */
  233. /* like to perform some action or just know when a DA is opened in your */
  234. /* layer.Can be handy to track memory problems when a DA is opened */
  235. /* with an Option-open */
  236. void DoDaCall(MenuHandle themenu, long theit)
  237. {
  238.     long qq;
  239.     Str255 DAname;
  240.     GetMenuItemText(themenu, theit, DAname);
  241.     qq = OpenDeskAcc((ConstStr255Param)DAname);
  242. }
  243.  
  244. /* end DoDaCall */
  245.  
  246. /* DoDiskEvents just checks the error code from the disk mount, */
  247. /* and puts up the 'Format' dialog (through DIBadMount) if need be */
  248. /* You can do much more here if you care about what disks are */
  249. /* in the drive */
  250. void DoDiskEvents(long dinfo)                               /* hi word is error code, lo word is drive number */
  251. {
  252.     short hival, loval, tommy;
  253.     Point fredpoint =  {
  254.         40, 40
  255.     };
  256.     hival = HiWord(dinfo);
  257.     loval = LoWord(dinfo);
  258.     if (hival != noErr)                                     /* something happened */ {
  259.         tommy = DIBadMount(fredpoint, dinfo);
  260.     }
  261. }
  262.  
  263. /* draws my window.Pretty simple */
  264. void DrawMain(WindowPtr drawIt)
  265. {
  266.     RgnHandle tempRgn;
  267.     Rect scratchRect;
  268.     BeginUpdate(drawIt);
  269.     SetPort(drawIt);
  270.     scratchRect = drawIt->portRect;
  271.     scratchRect.top = scratchRect.bottom - 15;
  272.     scratchRect.left = scratchRect.right - 15;
  273.     tempRgn = NewRgn();
  274.     GetClip(tempRgn);
  275.     ClipRect(&scratchRect);
  276.     DrawGrowIcon(drawIt);
  277.     SetClip(tempRgn);
  278.     DisposeRgn(tempRgn);
  279.     
  280.     EndUpdate(drawIt);
  281. }
  282.  
  283. /* my menu action taker.It returns a Boolean which I usually ignore, but it */
  284. /* mught be handy someday */
  285. /* I usually use it in an application to determine if a keystroke was accepted */
  286. /* by a menu or whether it should be passed along to any other key acceptors */
  287. Boolean DoSelected(long val)
  288. {
  289.     short loval, hival;
  290.     Boolean returnVal = false;
  291.     loval = LoWord(val);
  292.     hival = HiWord(val);
  293.     
  294.     switch (hival) {                                        /* switch off the menu number selected */
  295.         case kAppleMenu:                                    /* Apple menu */
  296.             if (loval != 1) {                               /* if this was not About, it's a DA */
  297.                 DoDaCall(GetMenuHandle(kAppleMenu), loval);
  298.             } else {
  299.                 Alert(kAboutBox, nil);                      /* do about box */
  300.             }
  301.             returnVal = true;
  302.             break;
  303.             
  304.         case kFileMenu:                                     /* File menu */
  305.             switch (loval) {
  306.                 
  307.                 case kQuitItem:
  308.                     gQuit = true;                           /* onlyitem */
  309.                     returnVal = true;
  310.                     break;
  311.                 default:
  312.                     break;
  313.             }
  314.             break;
  315.             
  316.         case kEditMenu:
  317.             /* edit menu junk */
  318.             /* don't care */
  319.             
  320.             switch (loval) {
  321.             default:
  322.                 break;
  323.             }
  324.             break;
  325.             
  326.         case kToolsMenu:
  327.             /* add all your test stuff here */
  328.             
  329.             switch (loval) {
  330.                 case kTestMenuChooseType:
  331.                     AddAResourceType();
  332.                     break;
  333.                 default:
  334.                     break;
  335.             }
  336.             break;
  337.             
  338.         case kHMHelpMenuID:                                 /* Defined in Balloons.h */
  339.             /* I only care about this item.If anything else is returned here, I don't know what */
  340.             /* it is, so I leave it alone.Remember, the Help Manager chapter says that */
  341.             /* Apple reserves the right to add and change things in the Help menu */
  342.             if (loval == gHelpItem)
  343.                 SampleHelpDialog();
  344.             break;
  345.             
  346.     }
  347.     HiliteMenu(0);
  348.     return(returnVal);
  349. }
  350.  
  351. void DoDocumentClick()
  352. {
  353.     
  354. }
  355.  
  356. void InitAEStuff(void)
  357. {    
  358.     OSErr aevtErr = noErr;
  359.     long aLong = 0;
  360.     Boolean gHasAppleEvents = false;
  361.     /* Check this machine for AppleEvents.  If they are not here (ie not 7.0)
  362.     *   then we exit */
  363.     gHasAppleEvents = (Gestalt(gestaltAppleEventsAttr, &aLong) == noErr);
  364.     /* The following series of calls installs all our AppleEvent Handlers.
  365.     *   These handlers are added to the application event handler list that 
  366.     *   the AppleEvent manager maintains.  So, whenever an AppleEvent happens
  367.     *   and we call AEProcessEvent, the AppleEvent manager will check our
  368.     *   list of handlers and dispatch to it if there is one.
  369.     */
  370.     if (gHasAppleEvents) {
  371.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, 
  372.              NewAEEventHandlerProc(AEOpenHandler),0, false);
  373.              if (aevtErr)  ExitToShell();
  374.  
  375.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, 
  376.              NewAEEventHandlerProc(AEOpenDocHandler),0, false);
  377.              if (aevtErr)  ExitToShell();
  378.  
  379.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, 
  380.              NewAEEventHandlerProc(AEQuitHandler), 0, false);
  381.              if (aevtErr)  ExitToShell();
  382.  
  383.          aevtErr = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, 
  384.              NewAEEventHandlerProc(AEPrintHandler),0, false);
  385.              if (aevtErr)  ExitToShell();
  386.          
  387.        } 
  388.     else ExitToShell();
  389.     
  390. }
  391. /* end InitAEStuff */
  392.  
  393. /* I'm not doing error handling in this sample for clarities sake, you should. Hah, */
  394. /* easy for me to say, huh? */
  395. void DoHighLevel(EventRecord *AERecord)
  396. {
  397.     OSErr myErr;
  398.     myErr = AEProcessAppleEvent(AERecord);
  399.     
  400. }
  401.  
  402. /* end DoHighLevel */
  403.  
  404. /* This is the standard Open Application event.*/
  405. pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  406. {
  407.     WindowPtr myWindow;
  408.  
  409. #pragma unused (messagein,reply,refIn)
  410.     /* we of course don't do anything here in this simple app */
  411.     /* except open our window */
  412.     myWindow = AddNewWindow(kDocWindowResID);
  413.     
  414.     return(noErr);
  415. }
  416.  
  417. /* end AEOpenHandler */
  418.  
  419. /* Open Doc, opens our documents.Remember, this can happen at application start AND */
  420. /* anytime else.If your app is up and running and the user goes to the desktop, hilites one */
  421. /* of your files, and double-clicks or selects Open from the finder File menu this event */
  422. /* handler will get called. Which means you don't do any initialization of globals here, or */
  423. /* anything else except open then doc.*/
  424. /* SO-- Do NOT assume that you are at app start time in this */
  425. /* routine, or bad things will surely happen to you. */
  426.  
  427. pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  428. {
  429. #pragma unused (messagein,refIn,reply)
  430.     /* we of course don't do anything here */
  431.     return(errAEEventNotHandled);                           /* we have no docs, so no odoc events should come to us */
  432. }
  433.  
  434. pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  435. {                                                           /* no printing handler in yet, so we'll ignore this */
  436.     /* the operation is functionally identical to the ODOC event, with the additon */
  437.     /* of calling your print routine.*/
  438. #pragma unused (messagein,refIn,reply)
  439.     /* we of course don't do anything here */
  440.     return(errAEEventNotHandled);                           /* we have no docs, so no pdoc events should come to us */
  441. }
  442.  
  443. /* Standard Quit event handler, to handle a Quit event from the Finder, for example.*/
  444. /* ••••• DO NOT CALL EXITTOSHELL HERE ••••• or you will never have a happy life.*/
  445. /* OK, it's a few months after I wrote that comment, and I've seen a lot of code */
  446. /* come through DTS that calls ExitToShell from quit handlers.Let me explain... */
  447. /* When an AppleEvent Handler is called (like this quit handler) you are ALMOST */
  448. /* 100% in your application world.A5 is right, you can call any toolbox function, */
  449. /* you can call your own routines, everything _seems_ like you are in complete*/
  450. /* control.Well, almost but not quite.The routine has been dispatch to from the */
  451. /* AppleEvent Manager's space, so you _must_ return to that at some point! */
  452. /* Which is why you can't call ETS from here.When you call ExitToShell from an */
  453. /* AE Handler, the most likely thing that happens is the FInder quits, and your*/
  454. /* application keeps running.Which ain't what you want, y'know? */
  455. /* so, DON'T CALL EXITTOSHELL FROM AN APPLEEVENT HANDLER!!!!!!!!!!!!!! */
  456. pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  457. {
  458. #pragma unused (messagein,refIn,reply)
  459.     gQuit = true;
  460.     return(noErr);
  461. }
  462.  
  463. /* This is my sample help dialog.Does not do anything, expand as you need */
  464. void SampleHelpDialog(void)
  465. {
  466.     DialogPtr tdial = GetNewDialog(kSampHelp, nil, (WindowPtr)-1);
  467.     short itemhit = 0;
  468.     while (itemhit != 1) {
  469.         ModalDialog(nil, &itemhit);
  470.     }
  471.     DisposeDialog(tdial);
  472. }
  473.  
  474. WindowPtr AddNewWindow(short theID)
  475. {
  476.     windowCHandle setControls;
  477.     WindowPtr tempWP;
  478.     short cnt = 0;
  479.     tempWP = GetNewWindow(theID, 0, (WindowPtr)-1);         /* get a new window */
  480.     ((WindowPeek)tempWP)->windowKind = kMyDocumentWindow;       /* mark it as my document window */
  481.     setControls = (windowCHandle)NewHandleClear(sizeof(windowControl));     /* add our control structure to it */
  482.     SetWRefCon(tempWP, (long)setControls);                  /* stop stuffing refCon directly <ckh 1.0.3> */
  483.     HLock((Handle)setControls);                             /* lock it down while we fill it*/
  484.     
  485.     /* add pointers to our procedures for drawing, saving, and closing */
  486.     /* This way, all I need is one dispatch point for drawing, closing */
  487.     /* or whatever, I don't have to case off the window kind to go to the*/
  488.     /* correct routine.Kinda like object-oriented programming, but I won't */
  489.     /* admit that. */
  490.     (*setControls)->drawMe = (ProcPtr)DrawMain;
  491.     (*setControls)->clickMe = (ProcPtr)DoDocumentClick;
  492.     (*setControls)->sizeMe = (ProcPtr)SizeMain;
  493.     (*setControls)->generalData = NewHandle(0);
  494.     return(tempWP);
  495. }
  496.  
  497. void SizeMain(WindowPtr theWindow)
  498. {
  499.     WindowPtr tempWP;
  500.     GetPort(&tempWP);
  501.     InvalRect(&theWindow->portRect);
  502.     SetPort(tempWP);
  503. }
  504.  
  505. void NilProc(void)
  506. {
  507.     
  508. }
  509.  
  510. /* AddAResourceType asks for the resource type to add to the menu */
  511. /* it preflights to see if at least one exists, then passes of to MyAddResMenu */
  512. void AddAResourceType(void)
  513. {
  514.     Rect tempRect;
  515.     short tempItem;
  516.     Handle tempHandle;
  517.     Str15 myStr;
  518.     ResType theType;
  519.     short hitItem;
  520.     DialogPtr myDialog = GetNewDialog(kResTypeDialog, nil, (WindowPtr)-1);
  521.     
  522.     /* New System 7 dialog manager calls, see tech note #304 */
  523.     /* interface to these calls is in AddResMenu7.1.h */
  524.     SetDialogDefaultItem(myDialog, ok);
  525.     SetDialogCancelItem(myDialog, cancel);
  526.     SetDialogTracksCursor(myDialog, true);
  527.     
  528.     do {
  529.         ModalDialog(gModalFilterUPP, &hitItem);
  530.     }
  531.             while (hitItem != ok && hitItem != cancel);
  532.     
  533.     if (hitItem == ok) {
  534.         
  535.         /* get the type and hammer it */
  536.         GetDialogItem(myDialog, kResTypeEditLine, &tempItem, &tempHandle, &tempRect);
  537.         GetDialogItemText(tempHandle, myStr);
  538.         
  539.         /* make sure there are 4 characters here, please */
  540.         if (myStr[0] == 4) {
  541.             BlockMove((Ptr)&myStr[1], (Ptr)&theType, sizeof(theType));
  542.             /* reality check to see if there are any of these at this time */
  543.             tempItem = Count1Resources(theType);
  544.             if (tempItem) {
  545.                 /* clear the menu out before I add new things*/
  546.                 tempItem = CountMItems(GetMenuHandle(kToolsMenu));
  547.                 
  548.                 /* I want to add below the dividing line */
  549.                 tempItem -= kTestMenuDivider;
  550.                 while (tempItem) {
  551.                     DeleteMenuItem(GetMenuHandle(kToolsMenu), kTestMenuDivider + 1);
  552.                     
  553.                     tempItem--;
  554.                 }
  555.                 
  556.                 /* call my routine */
  557.                 MyAddResMenu(theType, GetMenuHandle(kToolsMenu), kTestMenuDivider);
  558.             } else {
  559.                 /* yell because there are none of that type around */
  560.                 ParamText((ConstStr255Param)myStr, (ConstStr255Param)"", (ConstStr255Param)"", (ConstStr255Param)"");
  561.                 CautionAlert(kNoneOfThatType, nil);
  562.             }
  563.         }
  564.     }
  565.     DisposeDialog(myDialog);
  566.     
  567. }
  568.  
  569. /* MyAddResMenu does, basically, what AddResmenu does, it does an */
  570. /* "insert sort" (one of the Comp. Sci. types around here told me to call it that) */
  571. /* of the names of the resources.It uses IUCompString for */
  572. /* International Reasons (KFJC 89.7, 12-3 Sundays) */
  573. /* The only thing it really does differently is includes items that don't have */
  574. /* names by including the 'unnamedX' string. */
  575. /* Also notice that, since I'm using InsertMenuItem I have to parse the*/
  576. /* name string and take out any menu manager meta-characters before I insert things */
  577. /* so I don't get strange command keys from a name like "Alert/Warning 1" */
  578.  
  579. void MyAddResMenu(ResType theType, MenuHandle theMenu, short afterItem)
  580. {
  581.     short originalCount = afterItem;
  582.     short resourceCount = Count1Resources(theType);
  583.     short index = 1;
  584.     short unnamedCount = 0;
  585.     short qq;
  586.     ResType theIndexType;
  587.     short theID;
  588.     Str255 theName;
  589.     Str32 unusedWord;
  590.     Str255 tempString;
  591.     Handle tempHandle;
  592.     Boolean oldResLoad = *((Boolean *)0xA5E);
  593.     Boolean addedIt = false;
  594.     
  595.     GetIndString(unusedWord, kGenStrings, kUnnamedString);
  596.     SetResLoad(false);                                      /* don't want to load the resources */
  597.     do {
  598.         tempHandle = Get1IndResource(theType, index);
  599.         theName[0] = 0;
  600.         GetResInfo(tempHandle, &theID, &theIndexType, theName);
  601.         /* Check the name, if no name tell it so... */
  602.         if (theName[0] == 0) {
  603.             BlockMove((Ptr)&unusedWord, (Ptr)&theName, unusedWord[0] + 1);
  604.             NumToString((long)unnamedCount, tempString);
  605.             if ((theName[0] + tempString[0]) < 254) {
  606.                 BlockMove((Ptr)&tempString[1], (Ptr)(&theName[1] + theName[0]), tempString[0]);
  607.                 theName[0] += tempString[0];
  608.             }
  609.             unnamedCount++;
  610.         }
  611.         CleanUpMetaChars(theName);
  612.         /* here's where you sort in the menu items */
  613.         /* it goes roughly like.... */
  614.         for (qq = afterItem + 1; qq < CountMItems(theMenu) + 1; qq++) {
  615.             GetMenuItemText(theMenu, qq, tempString);
  616.             if (IUCompString(theName, tempString) == -1) {
  617.                 /* new string is less than an existing name */
  618.                 /* so insert it before it */
  619.                 InsertMenuItem(theMenu, theName, qq - 1);
  620.                 addedIt = true;
  621.                 break;                                      /* leave the 'for' loop */
  622.             }
  623.         }
  624.         /* If it didn't sort in, just add it to the end of the menu */
  625.         if (!addedIt)
  626.             InsertMenuItem(theMenu, theName, CountMItems(theMenu) + 1);
  627.         addedIt = false;
  628.         index++;
  629.     }
  630.             while (index <= resourceCount);
  631.     SetResLoad(oldResLoad);
  632. }
  633.  
  634. /* Some dialog filtering things */
  635. /* TabRetEsc toggles the cancel or OK button for keystrokes, and */
  636. /* treats a Tab press as selecting the whole edit line contents */
  637.  
  638. Boolean TabRetEsc(DialogPtr inputDialog, EventRecord *myDialogEvent, short *theDialogItem)
  639. {
  640.     Boolean returnValue = false;
  641.     long tilticks;
  642.     WindowPtr tempWindowPtr;
  643.     char theKey = myDialogEvent->message &charCodeMask;
  644.     GetPort(&tempWindowPtr);
  645.     SetPort(inputDialog);
  646.     switch (theKey) {
  647.         case kReturnKey:
  648.         case kEnterKey:                                     /* enter key */
  649.             /* This filters for Return or Enter as item 1, and Esc as item 2 */
  650.             *theDialogItem = ok;                            /* change whatever the current item is to the OK item */
  651.             /* now we need to invert the button */
  652.             HiliteControl(SnatchHandle(inputDialog, ok), 10);
  653.             Delay(8, &tilticks);                            /* wait about 8 ticks so they can see it */
  654.             HiliteControl(SnatchHandle(inputDialog, ok), false);
  655.             returnValue = true;
  656.             break;
  657.             /* This filters the escape key as the same as item 2 (the canx button, usually ) */
  658.         case kEscKey:
  659.             *theDialogItem = cancel;
  660.             HiliteControl(SnatchHandle(inputDialog, cancel), 10);
  661.             Delay(8, &tilticks);                            /* wait about 8 ticks so they can see it */
  662.             HiliteControl(SnatchHandle(inputDialog, cancel), false);
  663.             returnValue = true;
  664.             break;
  665.         case kTabKey:
  666.             /* I'm filtering the tab key here so a tab selects all the */
  667.             /* text in the edit line, as a convinience */
  668.             SelectDialogItemText(inputDialog, kResTypeEditLine, 0, 5);
  669.             returnValue = true;                             /* don't allow edit line swaps */
  670.             break;
  671.     }
  672.     SetPort(tempWindowPtr);
  673.     
  674.     return(returnValue);
  675.     
  676. }
  677.  
  678. /* Main dialog filter */
  679. pascal Boolean filterIt(DialogPtr inputDialog, EventRecord *myDialogEvent, short *theDialogItem)
  680. {
  681.     
  682.     Boolean returnVal = false;
  683.     ModalFilterUPP theModalProc;
  684.     char theKey;
  685.     Rect tempRect;
  686.     short tempItem;
  687.     Handle tempHandle;
  688.     char tempKey;
  689.     Str255 myStr;
  690.     long offset;
  691.     short selection;
  692.     long scrapLen;
  693.     short resultLen;
  694.     Boolean wasAKey = false;
  695.     if ((myDialogEvent->what == updateEvt) && (myDialogEvent->message != (UInt32)inputDialog)) {
  696.         /* May be a pending update event for another window, handle it so */
  697.         /* other layers get time */
  698.         windowCHandle tempWCH;
  699.         WindowPtr theWindow = (WindowPtr)myDialogEvent->message;
  700.         /* draw my window if it's really mine */
  701.         if (((WindowPeek)theWindow)->windowKind == kMyDocumentWindow) {
  702.             tempWCH = (windowCHandle)GetWRefCon(theWindow);
  703.             (ProcPtr)((*tempWCH)->drawMe)((WindowPtr)theWindow);
  704.             
  705.             *theDialogItem = 0;
  706.             returnVal = true;
  707.         }
  708.     } else {
  709.         wasAKey = (myDialogEvent->what == keyDown) || (myDialogEvent->what == autoKey);
  710.         
  711.         if ((wasAKey)) {
  712.             /* first check on the Big Three, tab, return, and escape */
  713.             if (!(returnVal = TabRetEsc(inputDialog, myDialogEvent, theDialogItem))) {
  714.                 
  715.                 /* not one of those, see if this keystroke will fit */
  716.                 /* in the edit line (restricted to 4 characters for a ResType) */
  717.                 
  718.                 /* Here I'm seeing if anything is selected, and getting the length */
  719.                 /* of the desk scrap in case the user presses */
  720.                 /* Copy or Paste keys */
  721.                 
  722.                 selection = HasSelectionRange((DialogPeek)inputDialog);
  723.                 scrapLen = GetScrap(nil, 'TEXT', &offset);
  724.                 
  725.                 GetDialogItem(inputDialog, kResTypeEditLine, &tempItem, &tempHandle, &tempRect);
  726.                 GetDialogItemText(tempHandle, myStr);
  727.                 
  728.                 /* calculate the result of adding the scrap to the current record.We use */
  729.                 /* this in a few places here */
  730.                 resultLen = myStr[0] + (scrapLen - selection);
  731.                 
  732.                 theKey = myDialogEvent->message & charCodeMask;
  733.                 tempKey = theKey;
  734.                 if (tempKey >= 0x61 && tempKey <= 0x7a)
  735.                     tempKey -= 0x20;
  736.                 
  737.                 if (myStr[0] > 3) {                         /* over 4, see what it is */
  738.                     
  739.                     if (IsEditKey(theKey, myDialogEvent->modifiers)) {
  740.                         /* it was an editing key, but it MAY be a command-V. */
  741.                         /* If it's a command-V then we won't allow it UNLESS */
  742.                         /* there is a slection range AND the new data won't overrun things */
  743.                         if ((tempKey == 'V') && (myDialogEvent->modifiers & cmdKey)) {
  744.                             
  745.                             /* this was a paste.check selection range and scrap len*/
  746.                             if (resultLen < 4) {
  747.                                 returnVal = false;          /* net result is4 or less */
  748.                             } else {
  749.                                 SysBeep(1);
  750.                                 returnVal = true;
  751.                             }
  752.                         } else {
  753.                             returnVal = false;              /* don't filter out editing keys */
  754.                         }
  755.                     } else {
  756.                         /* One more check (this can get complicated, huh?) */
  757.                         /* We now look to see if there is a selection range.If there */
  758.                         /* is a range of 1 or more characters, then the one character they are entering */
  759.                         /* now will replace that, and we'll end up with _less_ than 4 (or equal) */
  760.                         /* to do this, we have to get the TERecord out of the dialog.*/
  761.                         /* I'm going to do this in a seperate function */
  762.                         if (selection == nil) {
  763.                             SysBeep(1);                     /* complain a little */
  764.                             returnVal = true;               /* tell the dialog manager that we handled this already and */
  765.                             /* it doesn't have to, so the keystroke will _not_ get */
  766.                             /* added to the edit line */
  767.                         }
  768.                     }
  769.                 } else {
  770.                     /* even if we're less than 4 currently, a Command-V (paste) could put us over */
  771.                     /* so check it out */
  772.                     
  773.                     if ((tempKey == 'V') && (myDialogEvent->modifiers & cmdKey)) {
  774.                         
  775.                         /* Gettting the scrap with a nil handle, which does not give us data, just */
  776.                         /* returns the size */
  777.                         if (resultLen > 3) {
  778.                             SysBeep(1);                     /* complain a little */
  779.                             returnVal = true;               /* tell the dialog manager that we handled this already and */
  780.                             
  781.                         }
  782.                     }
  783.                 }
  784.             }
  785.         }
  786.     }
  787.     if (!returnVal) {
  788.         
  789.         OSErr myErr = GetStdFilterProc(&theModalProc);
  790.         if (myErr == noErr)
  791.             returnVal = CallModalFilterProc(theModalProc, inputDialog, myDialogEvent, theDialogItem);
  792.     }
  793.     return(returnVal);
  794. }
  795.  
  796. short HasSelectionRange(DialogPeek inputDialog)
  797. {
  798.     TEHandle theTERecord = inputDialog->textH;
  799.     short returnVal = (*theTERecord)->selEnd -(*theTERecord)->selStart;
  800.     
  801.     return(returnVal);
  802. }
  803.  
  804. /* end HasSelectionRange */
  805.  
  806. /* a little utility to see if the current key is an edit-type key */
  807. Boolean IsEditKey(char theKey, short modifiers)
  808. {
  809.     register qq;
  810.     Boolean returnVal = false;
  811.     char editChars[] =  {
  812.         kLeftArrow, kUpArrow, kRightArrow, kDownArrow, kBackSpace, kEscKey
  813.     };
  814.     char commandEdits[] =  {
  815.         'C', 'V', 'P'
  816.     };
  817.     for (qq = 0; qq < sizeof(editChars) / sizeof(char); qq++) {
  818.         if (theKey == editChars[qq])
  819.             returnVal = true;
  820.     }
  821.     if (returnVal != true && (modifiers & cmdKey)) {
  822.         /* check for XCP */
  823.         /* Do you want me to use 'toupper()'?What! And link in all of StdLib! aggggg */
  824.         if (theKey >= 0x61 && theKey <= 0x7a)
  825.             theKey -= 0x20;
  826.         for (qq = 0; qq < sizeof(commandEdits) / sizeof(char); qq++) {
  827.             if (theKey == commandEdits[qq])
  828.                 returnVal = true;
  829.         }
  830.     }
  831.     return(returnVal);
  832. }
  833.  
  834. /* end IsEditKey */
  835.  
  836. /* Gets the ControlHandle for the item you want in the dialog box thebox.*/
  837. /* Handy for setting checkboxes and radio buttons */
  838. /* This is the _most_ copied routine from this file */
  839. ControlHandle SnatchHandle(DialogPtr thebox, short theGetItem)
  840. {
  841.     short itemtype;
  842.     Rect itemrect;
  843.     Handle thandle;
  844.     
  845.     GetDialogItem(thebox, theGetItem, &itemtype, &thandle, &itemrect);
  846.     return((ControlHandle)thandle);
  847. }
  848.  
  849. /* end SnatchHandle */
  850.  
  851. void CleanUpMetaChars(Str255 theString)
  852. {
  853.     char metaKeys[] =  {
  854.         ';', '^', '!', '<', '/', '('
  855.     };
  856.     register qq, ii;
  857.     
  858.     qq = theString[0];
  859.     while (qq) {
  860.         for (ii = 0; ii < sizeof(metaKeys) / sizeof(char); ii++) {
  861.             if (theString[qq] == metaKeys[ii])
  862.                 theString[qq] = '•';                         /* replace with a Spot */
  863.         }
  864.         qq--;
  865.     }
  866. }
  867.  
  868.  
  869. #pragma segment Initialize
  870. void InitalizeApp(void)
  871. {
  872.     Handle myMenu;
  873.     MenuHandle helpHandle, appleMenuHandle;
  874.     StringHandle helpString;
  875.     short count;
  876.     long vers;
  877.     MaxApplZone();
  878.     InitGraf((Ptr)&qd.thePort);
  879.     InitFonts();
  880.     InitWindows();
  881.     InitMenus();
  882.     TEInit();
  883.     InitDialogs(nil);
  884.     InitCursor();
  885.     /* Check system version */
  886.     Gestalt(gestaltSystemVersion, &vers);
  887.     vers = (vers >> 8) & 0xf;                               /* shift result over and mask out major version number */
  888.     if (vers < 7) {
  889.         StopAlert(kBadSystem, nil);
  890.         ExitToShell();
  891.     }
  892.     InitAEStuff();
  893.     
  894.     /* set up the UPP for our dialog filter proc */
  895.     gModalFilterUPP = NewModalFilterProc(filterIt);
  896.   
  897.     /* set up my menu junk */
  898.     myMenu = GetNewMBar(kMBarID);
  899.     SetMenuBar(myMenu);
  900.     appleMenuHandle = GetMenuHandle(kAppleMenu);
  901.     AppendResMenu(appleMenuHandle, 'DRVR');
  902.     
  903.     /* now install my Help menu item in the Help Manager's menu */
  904.     HMGetHelpMenuHandle(&helpHandle);                       /* Get the Hlpe menu handle */
  905.     count = CountMItems(helpHandle);                        /* How many items are there? */
  906.     helpString = GetString(kHelpString);                    /* get my help string */
  907.     DetachResource((Handle)helpString);                             /* detach it */
  908.     HNoPurge((Handle)helpString);
  909.     MoveHHi((Handle)helpString);
  910.     HLock((Handle)helpString);
  911.     InsertMenuItem(helpHandle, (ConstStr255Param)helpString, count + 1);       /* insert my item in the Help menu */
  912.     gHelpItem = CountMItems(helpHandle);                    /* The number of the item */
  913.     
  914.     DrawMenuBar();
  915.     GetCurrentProcess(&gOurSN);                             /* Get our process serial number for later use, if needed */
  916.     
  917. }
  918.  
  919.  
  920. #pragma segment Main
  921.